# -*- coding: UTF-8 -*-
# @description : python
# @Author : zyl
# @contact : zyl@mxsoft.com
# @Time : 2021/9/29 18:00
# @Remark: IP子网 数据库层
import logging
import time

from IPy import IP

from db.common.NetworkManage.IpAM import modify
from db.common.NetworkManage.IpAM.Ip import IPDB
from index import _
from bsmiddle import *
from mxsoftpy import DB
from mxsoftpy.TreeDB import symbol_map
from mxsoftpy.db_def.def_type import type_map
from mxsoftpy.exception import ParamsError, DataError, DBError, CError
from utils.conf.mxconfig import MxConfig
from mxsoftpy.db_def.def_tree import TRDB_OPKF_OPENEXIST
import json


class IpSubnetDB(DB):

    def GetCompany(self, **kwargs):
        if 'company' not in kwargs:
            raise DataError(_('获取公司失败'))
        return kwargs['company']

    def check_label(self, label):
        """
        检查名称是否重复
        :param label:
        :return:
        """
        QIS = [{"name": "label", "nCondition": symbol_map['e'], "vLiData": label,
                "vLiDataType": type_map['str']}]
        daataCount = self.tree.exec_bs('bs_treedb_query_subkey_by_condition', QIS, '', [], 0, 0)
        if daataCount > 0:
            return True
        return False

    #@modify
    def create(self, **kwargs) -> int:
        """
        添加IP子网信息
        :param kwargs:
        :return:
        """
        try:
            lip = IP(kwargs['SubnetIp']).int()
            lmask = IP(kwargs['SubnetMask']).int()
            ipkey = IP(kwargs['SubnetIp']).make_net(kwargs['SubnetMask'])
        except Exception as e:
            raise Exception(_('IP或掩码输入有误, %s' % str(e)))
        company = self.GetCompany(**kwargs)
        path = company + '\\Net\\' + str(kwargs['id'])
        sub_key = self.tree.open('Co', path, file='ipctrl', host=MxConfig.HOST, port=MxConfig.PORT).sub_keys()
        self.tree.begin()
        if 'SubNet' not in sub_key:
            self.tree.insert_sub_keys('SubNet')
        kwargs['state'] = 0
        kwargs['ip'] = kwargs['SubnetIp']
        kwargs['mask'] = kwargs['SubnetMask']  # 意义在哪？
        id = kwargs['id']
        kwargs.pop('id')
        kwargs.pop('company')

        try:
            if 'SubNetId' in kwargs:
                self.tree.open('Co', path + '\\SubNet', file='ipctrl')
                QIS = [{"name": "SubNetId", "nCondition": symbol_map['ne'], "vLiData": kwargs['SubNetId'],
                        "vLiDataType": type_map['str']}]
                where = '0'
                QIS.append({"name": "ip", "nCondition": symbol_map['e'], "vLiData": kwargs['ip'],
                            "vLiDataType": type_map['str']})
                where += ' and 1'
                if kwargs['label']:
                    QIS.append({"name": "label", "nCondition": symbol_map['e'], "vLiData": kwargs['label'],
                                "vLiDataType": type_map['str']})
                    where += ' and 2'
                key = []
                self.tree.exec_bs('bs_treedb_query_subkey_by_condition', QIS, where, key, 0, 0)
                if key:
                    raise Exception(_('名称或IP已存在'))
                self.tree.open('Co', path + '\\SubNet\\' + kwargs['SubNetId'], file='ipctrl')
                self.tree.insert_items([(k, v) for k, v in kwargs.items()])
                self.tree.commit()
                ModifySubNet(company, id, kwargs['SubNetId'], 2)
            else:
                self.tree.open('Co', path + '\\SubNet', file='ipctrl')
                QIS = list()
                where_str = ''
                # 检查子网名称和IP是否重复
                if kwargs['label']:
                    QIS.append({"name": "label", "nCondition": symbol_map['e'], "vLiData": kwargs['label'],
                                "vLiDataType": type_map['str']})
                    where_str += ' 0'
                if kwargs['SubnetIp']:
                    SubnetIp = '.'.join(kwargs['SubnetIp'].split('.')[0:3])  # 取IP地址前三格
                    QIS.append(
                        {"name": "SubnetIp", "nCondition": symbol_map['nclike'],
                         "vLiData": '*' + SubnetIp + '.*',
                         "vLiDataType": type_map['str']})
                    where_str += ' or 1' if len(where_str) else '0'
                    key = []
                    self.tree.exec_bs('bs_treedb_query_subkey_by_condition', QIS, where_str, key, 0, 0)
                    if key:
                        raise Exception(_('名称或IP已存在'))
                self.tree.exec_tree('Treedb_InsertSubKey', str(ipkey), TRDB_OPKF_OPENEXIST)
                self.tree.exec_tree('Treedb_ReopenSubKey', path + '\\SubNet\\' + str(ipkey), TRDB_OPKF_OPENEXIST)
                self.tree.insert_items([(k, v) for k, v in kwargs.items()])
                self.tree.insert_item('SubNetId', str(ipkey), 'str')
                self.tree.insert_item('manage', 1, 'int')
                self.tree.insert_item('lIp', lip, 'int64')
                self.tree.insert_item('lMask', lmask, 'int64')
                self.tree.insert_item('new', 1, 'int')
                self.tree.commit()
                ModifySubNet(company, id, str(ipkey), 0)
                return True
        except Exception as e:
            self.tree.rollback()
            raise Exception(str(e))


    #@modify
    def ignore(self, **kwargs) -> str:
        """
        忽略IP子网
        :param kwargs: 前端参数
        """
        company = self.GetCompany(**kwargs)
        path = company + '\\Net\\' + kwargs['id'] + '\\SubNet\\' + kwargs['SubnetId']
        try:
            self.tree.open('Co', path, file='ipctrl', host=MxConfig.HOST, port=MxConfig.PORT).insert_items([('state', 1, 'int'), ('GroupId', '')])
        except DBError as e:
            raise ParamsError(_('更新数据失败, %s' % str(e)))
        return kwargs['SubnetId']

    @staticmethod
    def __delete_device(company, network, subnet):
        """
        删除IP子网下所有设备
        """
        try:
            res = eval(GetIPJson(company, network, subnet))  # 从缓存获取指定公司、网络、子网下的ip
        except TypeError as e:
            logging.error('使用bsmiddle模块中的GetIPJson从缓存获取数据时，获取的数据无法使用eval序列化成字典，错误详情：%s' % str(e))
            raise CError(_('删除子网失败'))
        if res['data']:
            errors = IPDB().delete(company=company, network=network, ip=';'.join([i['ip'] for i in res['data']]))
            if errors:
                raise DataError(_('删除子网失败'))

    #@modify
    def delete(self, **kwargs) -> int:
        """
        删除IP子网
        :param kwargs: 前端参数
        :return:
        """
        company = self.GetCompany(**kwargs)
        path = company + '\\Net\\' + kwargs['id'] + '\\SubNet\\'
        if kwargs['SubnetId']:  # 只有列表中有值才执行操作，防止删除整个视频组
            try:
                if kwargs['SubnetId'].find(';') == -1:
                    self.tree.open('Co', path + kwargs['SubnetId'], file='ipctrl',
                                   host=MxConfig.HOST, port=MxConfig.PORT).delete()
                    self.__delete_device(company, kwargs['id'], kwargs['SubnetId'])
                    ModifySubNet(company, kwargs['id'], kwargs['SubnetId'], 1)
                else:
                    delIds = kwargs['SubnetId'].split(';')
                    for id in delIds:
                        self.tree.open('Co', path + id, file='ipctrl', host=MxConfig.HOST,
                                       port=MxConfig.PORT).delete()
                        self.__delete_device(company, kwargs['id'], id)
                        ModifySubNet(company, kwargs['id'], id, 1)
            except DBError as e:
                logging.error('删除网络失败, %s' % str(e))
                raise ParamsError(_('删除子网失败, %s' % str(e)))
        return True

    #@modify
    def retrieve(self, **kwargs):
        """
        回显数据
        :param kwargs:
        :return:
        """
        return self.tree.open('Co', self.ccu + '\\Net\\' + kwargs['id'], file='ipctrl',
                              host=MxConfig.HOST, port=MxConfig.PORT).items()

    #@modify
    def IpCount(self, **kwargs):
        """
        统计子网数量和在用IP
        :param kwargs:
        :return:
        """
        company = self.GetCompany(**kwargs)
        path = company + '\\Net'
        sub_key = self.tree.open('Co', '', file='ipctrl', host=MxConfig.HOST, port=MxConfig.PORT).sub_keys()
        if company not in sub_key:
            return 0, 0
        QIS = [{"name": "state", "nCondition": symbol_map['e'], "vLiData": 0,
                "vLiDataType": type_map['int']}]
        self.tree.exec_bs('bs_treedb_open_subkey', path, TRDB_OPKF_OPENEXIST)
        key = []
        self.tree.exec_bs('bs_treedb_query_subkey_by_condition', QIS, '', key, 0, 0)

        SubnetCount = 0
        IpCount = 0
        if len(key) > 0:
            for id in key:
                self.tree.exec_bs('bs_treedb_open_subkey', path + '\\' + id, TRDB_OPKF_OPENEXIST)
                keys = list()
                self.tree.exec_tree('Treedb_GetAllSubKeys', keys)
                if 'SubNet' in keys:
                    self.tree.exec_bs('bs_treedb_open_subkey', path + '\\' + id + '\\SubNet', TRDB_OPKF_OPENEXIST)
                subnet_key = []
                self.tree.exec_bs('bs_treedb_query_subkey_by_condition', QIS, '', subnet_key, 0, 0)
                if subnet_key:
                    SubnetCount += len(subnet_key)
                    data_dict = json.loads(GetGroupSubNetJson(company, id))
                    print(data_dict)
                    if data_dict is None:
                        continue
                    for subkey in subnet_key:
                        for v in data_dict['subnet']:
                            if subkey == v['id']:
                                IpCount += v['UsedIpCount']
        return SubnetCount, IpCount

    #@modify
    def SubnetRecovery(self, **kwargs):
        """
        恢复子网
        :param kwargs:
        :return:
        """
        scompany = self.GetCompany(**kwargs)
        path = scompany + '\\Net\\' + kwargs['id'] + '\\SubNet\\'
        try:
            # 恢复网络
            self.tree.open('Co', scompany + '\\Net\\' + kwargs['id'], file='ipctrl',
                           host=MxConfig.HOST, port=MxConfig.PORT).insert_item('state', 0)

            # SubnetId存在，只恢复该子网，SubnetId为空，恢复网络下所有子网
            if kwargs['SubnetId']:
                if kwargs['SubnetId'].find(';') == -1:
                    res_dict = self.tree.open('Co', path + kwargs['SubnetId'], file='ipctrl', host=MxConfig.HOST,
                                              port=MxConfig.PORT).items()
                    if 'GroupId' in res_dict:
                        self.tree.exec_tree('Treedb_DeleteProperty', 'GroupId')
                    self.tree.open('Co', path + kwargs['SubnetId'], file='ipctrl',
                                   host=MxConfig.HOST, port=MxConfig.PORT).insert_item('state', 0)
                else:
                    delIds = kwargs['SubnetId'].split(';')
                    for id in delIds:
                        res_dict = self.tree.open('Co', path + id, file='ipctrl', host=MxConfig.HOST,
                                                  port=MxConfig.PORT).items()
                        if 'GroupId' in res_dict:
                            self.tree.exec_tree('Treedb_DeleteProperty', 'GroupId')
                        self.tree.open('Co', path + id, file='ipctrl', host=MxConfig.HOST,
                                       port=MxConfig.PORT).insert_item('state', 0)
            else:
                keys = list()
                self.tree.exec_tree('Treedb_GetAllSubKeys', keys)
                if 'SubNet' in keys:
                    self.tree.exec_tree('Treedb_ReopenSubKey', scompany + '\\Net\\' + kwargs['id'] + '\\SubNet',
                                        TRDB_OPKF_OPENEXIST)
                    QIS = [{"name": "state", "nCondition": symbol_map['e'], "vLiData": 1,
                            "vLiDataType": type_map['int']}]
                    subnetkey = list()
                    self.tree.exec_bs('bs_treedb_query_subkey_by_condition', QIS, '', subnetkey, 0, 0)
                    if subnetkey:
                        for val in subnetkey:
                            self.tree.exec_tree('Treedb_ReopenSubKey',
                                                scompany + '\\Net\\' + kwargs['id'] + '\\SubNet\\' + val,
                                                TRDB_OPKF_OPENEXIST)
                            self.tree.exec_bs('bs_treedb_insert_property', 'state', 0, type_map['int'], True)
                            self.tree.exec_bs('bs_treedb_insert_property', 'GroupId', '', type_map['str'], True)

            return True
        except DBError as e:
            return ParamsError(_('删除网络失败, %s' % str(e)))

    #@modify
    def ScanningAdd(self, **kwargs):
        """
        自动扫描设置
        :param kwargs:
        :return:
        """
        company = self.GetCompany(**kwargs)
        sub_key = self.tree.open('Co', '', file='ipctrl', host=MxConfig.HOST, port=MxConfig.PORT).sub_keys()
        if company not in sub_key:
            self.tree.exec_tree('Treedb_InsertSubKey', company, TRDB_OPKF_OPENEXIST)
            self.tree.exec_tree('Treedb_ReopenSubKey', company, TRDB_OPKF_OPENEXIST)
            self.tree.exec_bs('bs_treedb_insert_subkey', 'Config', TRDB_OPKF_OPENEXIST)
        else:
            self.tree.exec_tree('Treedb_ReopenSubKey', company, TRDB_OPKF_OPENEXIST)
            keys = list()
            self.tree.exec_tree('Treedb_GetAllSubKeys', keys)
            if 'Config' not in keys:
                self.tree.exec_bs('bs_treedb_insert_subkey', 'Config', TRDB_OPKF_OPENEXIST)
                self.tree.exec_tree('Treedb_ReopenSubKey', company + '\\Config', TRDB_OPKF_OPENEXIST)
                self.tree.exec_bs('bs_treedb_insert_subkey', 'timedTask', TRDB_OPKF_OPENEXIST)
                self.tree.exec_tree('Treedb_ReopenSubKey', company + '\\Config\\timedTask', TRDB_OPKF_OPENEXIST)
                self.tree.exec_bs('bs_treedb_insert_property', 'index', 1, type_map['int'], True)
                self.tree.exec_bs('bs_treedb_insert_subkey', str(1), TRDB_OPKF_OPENEXIST)
        self.tree.exec_tree('Treedb_ReopenSubKey', company + '\\Config\\timedTask\\1', TRDB_OPKF_OPENEXIST)
        self.tree.exec_tree('Treedb_DeleteAllProperty')
        kwargs.pop('company')
        kwargs.pop('type')
        cycle = {'1': [('TimeType', 0, 'int'), ('time', 1, 'int')], '2': [('TimeType', 0, 'int'), ('time', 2, 'int')],
                 '3': [('TimeType', 0, 'int'), ('time', 6, 'int')], '4': [('TimeType', 0, 'int'), ('time', 12, 'int')],
                 '5': [('TimeType', 0, 'int'), ('time', 24, 'int')], '6': [('TimeType', 1, 'int'), ('time', 7, 'int')]}
        # 'group': 'group.1,group.2',
        # rangData = [{'network': 'Net.1',  'subnet': '1,2'},
        #             {'network': 'Net.2', 'group': 'group.1,group.2', 'subnet': '1,2'}]

        rangeData = json.loads(kwargs['range'])
        tmp = []
        for k, val in enumerate(rangeData):
            k += 1
            tmp.append(('mx' + str(k) + 'network', val['network']))
            if 'group' in val:
                tmp.append(('mx' + str(k) + 'group', val['group']))
            if 'subnet' in val:
                tmp.append(('mx' + str(k) + 'subnet', val['subnet']))
        tmp.append(('rangeCount', len(rangeData), 'int'))
        for k, v in kwargs.items():
            if k == 'cycleType':
                if v == '7':
                    tmp.append(('TimeType', int(kwargs['TimeType']), 'int'))

                    tmp.append(('time', int(kwargs['time']), 'int'))
                else:
                    tmp.extend(cycle[v])
                tmp.append(('cycleType', int(kwargs['cycleType']), 'int'))
                continue
            tmp.append((k, v))
        # current_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
        # tmp.append(('iplastsearch',current_time))
        self.tree.insert_items(tmp)
        return True

    #@modify
    def GetScanning(self, **kwargs):
        """
        自动扫描设置获取
        :param kwargs:
        :return:
        """
        company = self.GetCompany(**kwargs)
        path = company
        sub_key = self.tree.open('Co', '', file='ipctrl', host=MxConfig.HOST, port=MxConfig.PORT).sub_keys()
        if company not in sub_key:
            return list()
        self.tree.exec_tree('Treedb_ReopenSubKey', path, TRDB_OPKF_OPENEXIST)
        keys = list()
        self.tree.exec_tree('Treedb_GetAllSubKeys', keys)
        if 'Config' not in keys:
            return list()
        self.tree.exec_tree('Treedb_ReopenSubKey', company + '\\Config\\timedTask\\1', TRDB_OPKF_OPENEXIST)
        res_dict = {}
        self.tree.exec_bs('bs_treedb_get_properties', [], res_dict)
        res_dict = {k:v for k,v in res_dict.items() if k not in ['iplastsearch','iplastsearchstr']}
        return res_dict

    #@modify
    def GetNetwork(self, **kwargs):
        """
        获取扫描网络数据
        :param kwargs:
        :return:
        """
        company = self.GetCompany(**kwargs)
        path = company + '\\Net\\' + kwargs['id']
        # keys = self.tree.open('Co', path, file='ipctrl', host=MxConfig.HOST, port=MxConfig.PORT).sub_keys()
        self.tree.open('Co', path + '\\ts', file='ipctrl', host=MxConfig.HOST, port=MxConfig.PORT)
        data = dict()
        data['subnet'] = list()
        # if 'SubNet' not in keys:
        #     data['subnet'] = list()
        # else:
        #     self.tree.exec_tree('Treedb_ReopenSubKey', path + '\\SubNet', TRDB_OPKF_OPENEXIST)
        #     QIS = [{"name": "state", "nCondition": symbol_map['e'], "vLiData": 0,
        #             "vLiDataType": type_map['int']}]
        #     key = []
        #     self.tree.exec_bs('bs_treedb_query_subkey_by_condition', QIS, '', key, 0, 0)
        #
        #     if key:
        #         result = {}
        #         self.tree.exec_bs('bs_treedb_get_subkey_properties', key, [], result)
        #         data['subnet'] = list(result.values())
        # self.tree.exec_tree('Treedb_ReopenSubKey', path + '\\ts', TRDB_OPKF_OPENEXIST)
        TsResult = {}
        self.tree.exec_bs('bs_treedb_get_subkey_properties', [], [], TsResult)
        data['ts'] = list(TsResult.values())
        data['company'] = company
        data['network'] = kwargs['id']
        return data

    def loopData(self, data, groupid, result):
        """

        :param data:
        :return:
        """
        for key, val in data.items():
            if val['parent'] == groupid:
                result.append(key)
                self.loopData(data, key, result)

    def GetGroup(self, path, groupid):
        """
        获取分组下级
        :param path: 路径
        :param groupid: 组ID
        :return:
        """
        self.tree.exec_tree('Treedb_ReopenSubKey', path + '\\group', TRDB_OPKF_OPENEXIST)
        QIS = [{"name": "state", "nCondition": symbol_map['e'], "vLiData": 0,
                "vLiDataType": type_map['int']}]
        key = []
        self.tree.exec_bs('bs_treedb_query_subkey_by_condition', QIS, '', key, 0, 0)
        data = []
        if key:
            result = {}
            self.tree.exec_bs('bs_treedb_get_subkey_properties', key, [], result)
            self.loopData(result, groupid, data)
            data.append(groupid)
        return data

    #@modify
    def GetGroupInfo(self, **kwargs):
        """
        获取扫描组
        :param kwargs:
        :return:
        """
        company = self.GetCompany(**kwargs)
        path = company + '\\Net\\' + kwargs['id']
        keys = self.tree.open('Co', path, file='ipctrl', host=MxConfig.HOST, port=MxConfig.PORT).sub_keys()
        data = dict()
        if 'SubNet' not in keys:
            data['subnet'] = list()
        else:
            QIS = [{"name": "state", "nCondition": symbol_map['e'], "vLiData": 0,
                    "vLiDataType": type_map['int']}]
            condition = '0'
            conditions = '('
            groups = self.GetGroup(company + '\\Net\\' + kwargs['id'], kwargs['GroupId'])
            for key, id in enumerate(groups):
                QIS.append({"name": "GroupId", "nCondition": symbol_map['e'], "vLiData": id,
                            "vLiDataType": type_map['str']})
                conditions += str(key + 1) + ' or '
            condition += ' and' + conditions[:-3] + ' )'
            self.tree.exec_tree('Treedb_ReopenSubKey', path + '\\SubNet', TRDB_OPKF_OPENEXIST)
            key = []
            self.tree.exec_bs('bs_treedb_query_subkey_by_condition', QIS, condition, key, 0, 0)

            if key:
                result = {}
                self.tree.exec_bs('bs_treedb_get_subkey_properties', key, [], result)
                data['subnet'] = list(result.values())
            else:
                data['subnet'] = list()
        self.tree.exec_tree('Treedb_ReopenSubKey', path + '\\ts', TRDB_OPKF_OPENEXIST)
        TsResult = {}
        self.tree.exec_bs('bs_treedb_get_subkey_properties', [], [], TsResult)
        data['ts'] = list(TsResult.values())
        data['company'] = company
        data['network'] = kwargs['id']
        return data

    #@modify
    def GetSubnetInfo(self, **kwargs):
        """
        获取扫描子网
        :param kwargs:
        :return:
        """
        company = self.GetCompany(**kwargs)
        path = company + '\\Net\\' + kwargs['id']
        keys = self.tree.open('Co', path, file='ipctrl', host=MxConfig.HOST, port=MxConfig.PORT).sub_keys()
        if 'SubNet' not in keys:
            return 0, list()
        if kwargs['SubNetId'].find(';') != -1:
            SubNetId = kwargs['SubNetId'].split(';')
        else:
            SubNetId = [kwargs['SubNetId']]
        self.tree.exec_tree('Treedb_ReopenSubKey', path + '\\SubNet', TRDB_OPKF_OPENEXIST)
        res_dict = {}
        self.tree.exec_bs('bs_treedb_get_subkey_properties', SubNetId, [], res_dict)
        data = {'subnet': list(res_dict.values())}
        self.tree.exec_tree('Treedb_ReopenSubKey', path + '\\ts', TRDB_OPKF_OPENEXIST)
        TsResult = {}
        self.tree.exec_bs('bs_treedb_get_subkey_properties', [], [], TsResult)
        data['ts'] = list(TsResult.values())
        data['company'] = company
        data['network'] = kwargs['id']
        return data

    #@modify
    def FindIp(self, **kwargs):
        """
        发现IP
        :param kwargs:
        :return:
        """
        company = self.GetCompany(**kwargs)
        path = company + '\\Net'
        keys = self.tree.open('Co', '', file='ipctrl', host=MxConfig.HOST, port=MxConfig.PORT).sub_keys()
        if company not in keys:
            self.tree.exec_tree('Treedb_InsertSubKey', company, TRDB_OPKF_OPENEXIST)
            self.tree.exec_tree('Treedb_ReopenSubKey', company, TRDB_OPKF_OPENEXIST)
            self.tree.exec_bs('bs_treedb_insert_subkey', 'Net', TRDB_OPKF_OPENEXIST)
        self.tree.exec_tree('Treedb_ReopenSubKey', path, TRDB_OPKF_OPENEXIST)
        if 'findip' in kwargs:
            self.tree.exec_bs('bs_treedb_insert_property', 'findip', int(kwargs['findip']), type_map['int'], True)
            return True
        else:
            res_dict = {}
            self.tree.exec_bs('bs_treedb_get_properties', [], res_dict)
            return res_dict

    def scanning(self):
        try:
            self.tree.open('Co','Co_1\\AccountId\\110',file='ipctrl',host=MxConfig.HOST)
            self.tree.insert_item('type', 1)
        except:
            raise DataError(_('数据库配置错误'))

